package com.jzo2o.orders.manager.service.impl;

import cn.hutool.cache.impl.NoCache;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.Handler.OrdersHandler;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.redis.helper.CacheHelper;
import com.rabbitmq.client.Return;
import io.netty.handler.codec.redis.InlineCommandRedisMessage;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Or;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;
import static com.jzo2o.orders.base.constants.RedisConstants.RedisKey.ORDERS;
import static com.jzo2o.orders.base.constants.RedisConstants.Ttl.ORDERS_PAGE_TTL;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }
    @Resource
    CacheHelper cacheHelper;

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus())
                //指定ID列
                .select(Orders::getId);
                ;
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.查询订单列表
        //包含了所有ID -聚集索引
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        if (ObjectUtil.isEmpty(ordersPage.getRecords())) {
            return new ArrayList<>();
        }
        //提取订单ID list<long>形式
        List<Long> orderIds= CollUtils.getFieldValues(ordersPage.getRecords(), Orders::getId);

        //todo: 先查询缓存，缓存没有再查询数据库....

        String redisKey=String.format(ORDERS,currentUserId);
        //查询缓存
        List<OrderSimpleResDTO> orderSimpleResDTOS1 = cacheHelper.<Long, OrderSimpleResDTO>batchGet(

                redisKey,//目标数据类型 redisKey的一部分
                orderIds,//  目标数据唯一id 订单id列表
                (noCacheIds, clazz) -> {
                    //参数1 没有的id 参数2 不知道
                    List<Orders> ordersList = batchQuery(noCacheIds);
                    //防止缓存穿透
                    if (CollUtils.isEmpty(ordersList)) {
                        return new HashMap<>();
                    }
                    //转map
                    Map<Long, OrderSimpleResDTO> hashList = ordersList.stream().collect
                            (Collectors.toMap(Orders::getId, o -> BeanUtil.toBean(o, OrderSimpleResDTO.class)));
                    return hashList;//上层把该数据根据key -value形式传回

                },//匿名函数写法 -未包含数据ID进行的操作
                OrderSimpleResDTO.class,//返回的数据类型
                ORDERS_PAGE_TTL//过期时间
        );


        //  List<Orders> ordersList = batchQuery(orderIds);
        /**
         *   public List<Orders> batchQuery(List<Long> ids) {
         * LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
         * return baseMapper.selectList(queryWrapper);
         *         }
         */

        return orderSimpleResDTOS1;

    }
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */


    @Override
    public OrderSnapshotDTO getDetail(Long id) {
        //Orders orders = queryById(id);
        ///快照方法查询缓存
        String currentSnapshotCache = orderStateMachine.getCurrentSnapshotCache(String.valueOf(id));
        //将json转对象
        OrderSnapshotDTO orderSnapshotDTO=JSONUtil.toBean(currentSnapshotCache,OrderSnapshotDTO.class);

        //查询订单是否取消-懒加载方式
        orderSnapshotDTO =canalIfPayOvertime(orderSnapshotDTO);



        return orderSnapshotDTO;
    }
@Resource
    IOrdersCreateService ordersCreateService;
    private OrderSnapshotDTO canalIfPayOvertime(OrderSnapshotDTO orderSnapshotDTO) {
//拿出时间
        boolean before = orderSnapshotDTO.getCreateTime().plusMinutes(15).isBefore(LocalDateTime.now());

        if(before&& orderSnapshotDTO.getOrdersStatus()==OrderStatusEnum.NO_PAY.getStatus())
        {
            //查询支付结果，如果支付最新状态仍是未支付进行取消订单 --防止线程安全问题
            OrdersPayResDTO ordersPayResDTO = ordersCreateService.getPayResultFromTradServer(orderSnapshotDTO.getId());
            int payStatus = ordersPayResDTO.getPayStatus();
            if(payStatus!= OrderPayStatusEnum.PAY_SUCCESS.getStatus())
            {
                //未支付 -取消订单
                OrderCancelDTO orderCancelDTO=BeanUtil.toBean(orderSnapshotDTO,OrderCancelDTO.class);
                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
                orderCancelDTO.setCancelReason("订单超时支付，自动取消");
                cancel(orderCancelDTO);
                //从快照中查询订单数据
                String jsonResult = orderStateMachine.getCurrentSnapshotCache(String.valueOf(orderSnapshotDTO.getId()));
                orderSnapshotDTO = JSONUtil.toBean(jsonResult, OrderSnapshotDTO.class);
                return orderSnapshotDTO;
            }
        }
        return orderSnapshotDTO;
    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }
    @Resource
    IOrdersManagerService father;

    @Override
    public void cancel(OrderCancelDTO  orderCancelReqDTO) {
        //订单取消
        Orders orders = baseMapper.selectById(orderCancelReqDTO.getId());
        OrderSnapshotDTO orderSnapshotDTO = BeanUtil.toBean(orders, OrderSnapshotDTO.class);
        orderStateMachine.start(null,String.valueOf(orderCancelReqDTO.getId()),orderSnapshotDTO);
        if(ObjectUtil.isNull(orders))
        {
            throw new DbRuntimeException("找不到要取消的订单,订单号：{}",orderCancelReqDTO.getId());
        }
        //判断id是否支付
        Integer ordersStatus = orders.getOrdersStatus();
        if(OrderStatusEnum.NO_PAY.getStatus()==ordersStatus)
        {
            //待支付  事务写法
            father.cancelByNoPay(orderCancelReqDTO);

        }else if(OrderStatusEnum.DISPATCHING.getStatus()==ordersStatus)
        {
            father.cancelByDispatching(orderCancelReqDTO);
            //开辟线程退款
            ordersHandler.requestRefundNewThread(orders.getId());
            //支付
        }else
        {
            throw new CommonException("当前订单状态不支持取消");
        }


    }
@Resource
    IOrdersCanceledService ordersCanceledService;
    @Resource
    IOrdersCommonService iOrdersCommonService;
    @Resource
    private OrderStateMachine orderStateMachine;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelByNoPay(OrderCancelDTO orderCancelReqDTO) {
//状态事件变更
        OrdersCanceled ordersCanceled=BeanUtil.toBean(orderCancelReqDTO,OrdersCanceled.class);
        ordersCanceled.setCancellerId(orderCancelReqDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelReqDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelReqDTO.getCurrentUserType());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);
        OrderSnapshotDTO orderSnapshotDTO = BeanUtil.toBean(ordersCanceled, OrderSnapshotDTO.class);
        //

        ///为支付取消订单逻辑
        //1.更改订单取消表
        /**

        //2.更新订单状态

         */
        orderStateMachine.changeStatus(String.valueOf(orderCancelReqDTO.getId()), OrderStatusChangeEventEnum.PAYED,orderSnapshotDTO);


    }
@Resource
    IOrdersRefundService ordersRefundService;
    @Resource
    OrdersHandler ordersHandler;
    @Override
    public void cancelByDispatching(OrderCancelDTO orderCancelReqDTO) {
        //实现派单退款

        //1,添加取消记录表
        OrdersCanceled ordersCanceled = BeanUtil.toBean(orderCancelReqDTO, OrdersCanceled.class);
        ordersCanceled.setCancellerId(orderCancelReqDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelReqDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelReqDTO.getCurrentUserType());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);
        //更新订单状态未关闭
        OrderUpdateStatusDTO orderUpdateStatusDTO = OrderUpdateStatusDTO.builder().id(orderCancelReqDTO.getId())
                .originStatus(OrderStatusEnum.DISPATCHING.getStatus())
                .targetStatus(OrderStatusEnum.CLOSED.getStatus())
                .refundStatus(OrderRefundStatusEnum.REFUNDING.getStatus())//退款状态为退款中
                .build();
        //这个是自己的sql语句
        int result=iOrdersCommonService.updateStatus(orderUpdateStatusDTO);
        if(result<=0)
        {
            throw new DbRuntimeException("待服务订单出错");
        }

        //新增退款表
        OrdersRefund ordersRefund=new OrdersRefund();
        ordersRefund.setId(orderCancelReqDTO.getId());
        ordersRefund.setTradingOrderNo(orderCancelReqDTO.getTradingOrderNo());
        ordersRefund.setRealPayAmount(orderCancelReqDTO.getRealPayAmount());
        ordersRefundService.save(ordersRefund);




    }

}
